package online.chazi.bootplus.biz.utils;

import com.alibaba.fastjson.JSON;
import lombok.Data;
import online.chazi.bootplus.biz.utils.exceptions.BizException;
import org.springframework.lang.Nullable;

import java.lang.reflect.Method;

/**
 * 说明：means ReturnResult or ResponseResult
 * Created by 叉子同学 on 2020-04-11 18:23
 */
@Data
public class RR<T> {

    private static Enum<?> replyCodeEnum;

    private static final RR<?> OK = new RR<>();
    private static final RR<?> ERROR = RR.error();

    private int code = SimpleBizReplyCode.SUCCESS.code;
    //客户端应该做个判断，比如：tip((showCode ? code : "") + message);
    //让服务端可以控制输出，在应急的情况下有这样的业务需求。
    private boolean showCode = true;

    @Nullable
    private String message = SimpleBizReplyCode.SUCCESS.message;
    @Nullable
    private T body;

    public static void register(Enum<?>[] replyCodeEnums) {
        RR.register(replyCodeEnums[0]);
    }

    public static void register(Enum<?> replyCodeEnum) {
        if (RR.replyCodeEnum != null) {
            if (!RR.replyCodeEnum.getClass().getName().equals(replyCodeEnum.getClass().getName()))
                throw new IllegalArgumentException("replyCodeEnum 不能重复注册！");
        } else {
            if (!(replyCodeEnum instanceof IReplyCode))
                throw new IllegalArgumentException("replyCodeEnum 枚举必须实现 IReplyCode 接口！");
            RR.replyCodeEnum = replyCodeEnum;
        }
    }

    public RR<T> showCode(boolean show) {
        this.showCode = show;
        return this;
    }

    public RR<T> message(String message) {
        this.message = message;
        return this;
    }

    public RR<T> body(T body) {
        this.body = body;
        return this;
    }

    public static <T> RR<T> reply(T body) {
        RR<T> r = new RR<>();
        if (RR.replyCodeEnum != null) {
            Enum<?> ok = Utils.getSuccess(RR.replyCodeEnum);
            r.setCode(Utils.getCode(ok));
            r.setMessage(Utils.getMessage(ok));
        }
        r.body = body;
        return r;
    }

    public static <T> RR<T> reply(Enum<?> e) {
        return reply(e, null);
    }

    public static <T> RR<T> reply(Enum<?> e, T body) {
        register(e);
        RR<T> r = new RR<>();
        r.setCode(Utils.getCode(e));
        r.setMessage(Utils.getMessage(e));
        r.setBody(body);
        return r;
    }

    public static <T> RR<T> error() {
        RR<T> r = new RR<>();
        if (RR.replyCodeEnum == null) {
            r.setCode(SimpleBizReplyCode.ERROR.code);
            r.setMessage(SimpleBizReplyCode.ERROR.message);
        } else {
            Enum<?> err = Utils.getError(RR.replyCodeEnum);
            r.setCode(Utils.getCode(err));
            r.setMessage(Utils.getMessage(err));
        }
        return r;
    }

    public static <T> RR<T> error(String message) {
        RR<T> r = new RR<>();
        if (RR.replyCodeEnum == null)
            r.setCode(SimpleBizReplyCode.ERROR.code);
        else {
            Enum<?> err = Utils.getError(RR.replyCodeEnum);
            r.setCode(Utils.getCode(err));
        }
        r.setMessage(message);
        return r;
    }

    public static <T> RR<T> reply(BizException e) {
        RR<T> r = new RR<>();
        Enum<?> anEnum = e.getAnEnumReplyCode();
        r.setCode(Utils.getCode(anEnum));
        r.setMessage(Utils.getMessage(anEnum));
        return r;
    }

    public static class Utils {
        public static Integer getCode(Enum<?> e) {
            return (Integer) getValue(e, IReplyCode.GET_CODE_METHOD);
        }

        public static String getMessage(Enum<?> e) {
            return (String) getValue(e, IReplyCode.GET_MESSAGE_METHOD);
        }

        public static Enum<?> getSuccess(Enum<?> e) {
            return (Enum<?>) getValue(e, IReplyCode.GET_SUCCESS);
        }

        public static Enum<?> getError(Enum<?> e) {
            return (Enum<?>) getValue(e, IReplyCode.GET_ERROR);
        }

        public static Object getValue(Enum<?> e, String methodName) {
            try {
                Method method = e.getClass().getMethod(methodName);
                return method.invoke(e);
            } catch (Exception ex) {
                throw new IllegalArgumentException(ex);
            }
        }
    }

    public String toString() {
        return JSON.toJSONString(this);
    }

}
